# urllib 库用于操作网页 URL，并对网页的内容进行抓取处理。
"""
urllib 包 包含以下几个模块：
    urllib.request - 打开和读取 URL。
    urllib.error - 包含 urllib.request 抛出的异常。
    urllib.parse - 解析 URL。
    urllib.robotparser - 解析 robots.txt 文件。
"""
"""
可以使用 urllib.request 的 urlopen 方法来打开一个 URL，语法格式如下：
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
    url：url 地址。
    data：发送到服务器的其他数据对象，默认为 None。
    timeout：设置访问超时时间。
    cafile 和 capath：cafile 为 CA 证书， capath 为 CA 证书的路径，使用 HTTPS 需要用到。
    cadefault：已经被弃用。
    context：ssl.SSLContext类型，用来指定 SSL 设置。
"""
import urllib.robotparser
from urllib.parse import urlparse
import urllib.parse
from urllib import request
import urllib.request
from urllib.request import OpenerDirector, urlopen
url = urlopen("http://www.baidu.com")
# 使用 read() 函数获取网页的 HTML 实体代码。
# print(url.read())
# 指定读取的长度：
# print(url.read(400))
# 使用getcode()函数获取网页状态码
print(url.getcode())

# 将网页内容读取保存至本地
# f = open("resource/test.html","wb")
# content = url.read()
# f.write(content)
# f.close()

# url的编码与解码
# 编码
encode_url = urllib.request.quote('https://www.baidu.com')
print(encode_url)
# 解码
unencode_url = urllib.request.unquote(encode_url)
print(unencode_url)

"""
抓取网页一般需要对 headers（网页头信息）进行模拟，这时候需要使用到 urllib.request.Request 类：
class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
    url：url 地址。
    data：发送到服务器的其他数据对象，默认为 None。
    headers：HTTP 请求的头部信息，字典格式。
    origin_req_host：请求的主机地址，IP 或域名。
    unverifiable：很少用整个参数，用于设置网页是否需要验证，默认是False。。
    method：请求方法， 如 GET、POST、DELETE、PUT等。
"""
url = "https://www.runoob.com/?s="  # 菜鸟教程搜索页
keyword = "图片"
# 对请求进行编码
key_code = urllib.request.quote(keyword)
print(key_code)
url_all = url+key_code
print(url_all)
# 头部信息
header = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36 Edg/93.0.961.47'
}
request = urllib.request.Request(url_all, headers=header)
reponse = urllib.request.urlopen(request).read()
f = open('resource/search.html', 'wb')
f.write(reponse)
f.close()

# 表单 POST 传递数据
url = "https://www.runoob.com/try/py3/py3_urllib_test.php"  # 提交到表单页面
data = {'name': 'Daniel', 'tag': '菜鸟'}
header = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36 Edg/93.0.961.47'
}
data = urllib.parse.urlencode(data).encode(
    'utf-8')  # 对参数进行编码，解码使用 urllib.parse.urldecode
request = urllib.request.Request(url, data, header)  # 请求处理
reponse = urllib.request.urlopen(request).read()  # 读取结果
f = open('resource/post.html', 'wb')
f.write(reponse)
f.close()

'''
urllib.error 包含了两个方法，URLError 和 HTTPError。
URLError 是 OSError 的一个子类，用于处理程序在遇到问题时会引发此异常（或其派生的异常），包含的属性 reason 为引发异常的原因。
HTTPError 是 URLError 的一个子类，用于处理特殊 HTTP 错误例如作为认证请求的时候，包含的属性 code 为 HTTP 的状态码， reason 为引发异常的原因，headers 为导致 HTTPError 的特定 HTTP 请求的 HTTP 响应头。
'''
# 抓取不存在的网页
url = urllib.request.urlopen('https://www.baidu.com')
print(url.getcode())

try:
    url1 = urllib.request.urlopen('https://www.runoob.com/no.html')
except urllib.error.HTTPError as e:
    if e.code == 404:
        print('网页状态码：404')

# urllib.parse 用于解析 URL，格式如下：urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)
# urlstring 为 字符串的 url 地址，scheme 为协议类型，
# allow_fragments 参数为 false，则无法识别片段标识符。相反，它们被解析为路径，参数或查询组件的一部分，并 fragment 在返回值中设置为空字符串。
o = urlparse('https://www.runoob.com/?s=%E5%9B%BE%E7%89%87')
print(o)  # 内容是一个元组，包含 6 个字符串：协议，位置，路径，参数，查询，判断。
print(o.scheme)

'''
urllib.robotparser 用于解析 robots.txt 文件。
robots.txt（统一小写）是一种存放于网站根目录下的 robots 协议，它通常用于告诉搜索引擎对网站的抓取规则。
urllib.robotparser 提供了 RobotFileParser 类，语法如下：class urllib.robotparser.RobotFileParser(url='')
'''
# 这个类提供了一些可以读取、解析 robots.txt 文件的方法：
# set_url(url) - 设置 robots.txt 文件的 URL。
# read() - 读取 robots.txt URL 并将其输入解析器。
# parse(lines) - 解析行参数。
# can_fetch(useragent, url) - 如果允许 useragent 按照被解析 robots.txt 文件中的规则来获取 url 则返回 True。
# mtime() - 返回最近一次获取 robots.txt 文件的时间。 这适用于需要定期检查 robots.txt 文件更新情况的长时间运行的网页爬虫。
# modified() - 将最近一次获取 robots.txt 文件的时间设置为当前时间。
# crawl_delay(useragent) - 为指定的 useragent 从 robots.txt 返回 Crawl-delay 形参。 如果此形参不存在或不适用于指定的 useragent 或者此形参的 robots.txt 条目存在语法错误，则返回 None。
# request_rate(useragent) - 以 named tuple RequestRate(requests, seconds) 的形式从 robots.txt 返回 Request-rate 形参的内容。 如果此形参不存在或不适用于指定的 useragent 或者此形参的 robots.txt 条目存在语法错误，则返回 None。
# site_maps() - 以 list() 的形式从 robots.txt 返回 Sitemap 形参的内容。 如果此形参不存在或者此形参的 robots.txt 条目存在语法错误，则返回 None。

